<?php

namespace App\Lib\Mongodb;

use App\Core\Exception\DefaultException;
use EasySwoole\Component\Singleton;

use MongoDB\BSON\ObjectId;
use MongoDB\Driver\Command;
use MongoDB\Driver\Manager;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Query;


class Mongodb
{
    use Singleton;

    private $mongo = ''; //链接mongodb实例

    private $database; //数据库

    public function __construct($config = null)
    {
        try{
            $mongoServer = "mongodb://";
            if ($config['username']) {
                $mongoServer .= $config['username'] . ':' . urlencode($config['password']) . '@';
            }
            $mongoServer .= $config['hostname'];
            if ($config['port']) {
                $mongoServer .= ':' . $config['port'] . '/' . $config['database'];
            }

            $this->database = $config['database'];

            $this->mongo = new Manager($mongoServer);
        }catch (\Exception $e){
            throw new DefaultException('链接失败');
        }
    }


    public function insert($collection, $data)
    {

        $BulkWrite = new BulkWrite();
        if (is_array(current($data))) {
            foreach ($data as $key => $datum) {
                $_id = new ObjectID();
                $datum['_id'] = $_id;
                $BulkWrite->insert($datum);
            }
        } else {
            $_id = new ObjectID();
            $data['_id'] = $_id;
            $BulkWrite->insert($data);
        }

        //返回结果_id
        $_ids = (string)$_id;

        $res = $this->mongo->executeBulkWrite($this->database . "." . $collection, $BulkWrite);

        $res->getInsertedCount();

        return $_ids;
    }

    public function update($collection,$data,$filter){
        $BulkWrite=new BulkWrite();
        if(is_array(current($filter))){
            foreach ($filter as $key=>$value){
                if (isset($value['_id'])) {
                    $value['_id'] = new ObjectId($value['_id']);
                }
                $BulkWrite->update($value,$data[$key]);
            }
        }else{
            if (isset($filter['_id'])) {
                $filter['_id'] = new ObjectId($filter['_id']);
            }
            $BulkWrite->update($filter,$data);
        }

        //更新操作
        $res = $this->mongo->executeBulkWrite($this->database.".".$collection,$BulkWrite);

        //影响记录数
        $res->getModifiedCount();

        return empty($res->getModifiedCount()) ? false : true;

    }


    public function delete($collection, $filter)
    {
        $BulkWrite = new BulkWrite();

        if (is_array(current($filter))) {
            foreach ($filter as $key => $value) {
                if (isset($value['_id'])) {
                    $value['_id'] = new ObjectId($value['_id']);
                }
                $BulkWrite->delete($value);
            }
        }else {
            if (isset($filter['_id'])) {
                $filter['_id'] = new ObjectId($filter['_id']);
            }
            $BulkWrite->delete($filter);
        }
        $res = $this->mongo->executeBulkWrite($this->database . "." . $collection, $BulkWrite);

        //删除记录数
        $res->getDeletedCount();

        return empty($res->getDeletedCount()) ? false : true;
    }

    public function select($collection, $filter,$pagination=null,$order=[])
    {
        $option = [];

        //处理分页
        if(isset($pagination['page_size'])){
            $pageSize = $pagination['page_size'];
            $page = $pagination['page'] ?? 1;

            $page > 0 || $page = 1;
            $pageSize > 0 || $pageSize = 20;
            $offset = ($page - 1) * $pageSize;

            //查询多少条记录
            $option['limit'] = $pageSize;

            //略过前面多少条记录
            if ( !empty($offset) )$option['skip'] = $offset;
        }

        //处理排序
        if ( !empty($order) ){
            if ($order[1] == 'desc') {
                $option['sort'] = [$order[0] => -1];
            }else {
                $option['sort'] = [$order[0] => 1];
            }
        }

        $Query = new Query($filter,$option);

        $cursor = $this->mongo->executeQuery($this->database . "." . $collection, $Query);

        $rows = json_decode(json_encode($cursor->toArray()), true);

        foreach ($rows as $key => $document) {
            $rows[$key]['_id']= $document['_id']['$oid'];
        }

        return $rows;
    }

    public function count($collection, $filter){
        $command = new Command(
            array(
                "count" => $collection,
                "query" => $filter,
            )
        );

        $count =  $this->mongo->executeCommand($this->database , $command)->toArray()[0]->n;

        return $count;
    }

    public function createTable($collection)
    {
        //查询数据表是否存在
        $command = new Command(['listCollections' => $this->database]);
        $cursor = $this->mongo->executeCommand($this->database , $command);
        $response = $cursor->toArray();
        $exists = false;
        foreach ($response as $item) {
            if ( $item->name == $collection ) $exists = true;
        }

        if ( $exists ) return true;

        //不存在，创建数据表
        $cmd = [
            'createIndexes' => $collection,//集合名             
            'indexes'       => [
                [
                    'name' => 'id',
                    'key'  => ['id' => 1]
                ]
            ],
        ];

        $command = new Command($cmd);
        $cursor = $this->mongo->executeCommand($this->database , $command);

        return $cursor;
    }

    public function executeCommand(array $command){
        $command = new Command($command);

        $cursor = $this->mongo->executeCommand($this->database , $command);
        $response = $cursor->toArray();

        return $response;
    }

    public function insertMulti($collection,$data){
        $BulkWrite = new BulkWrite();
        foreach ($data as $action) {
            $BulkWrite->insert($action);
        }
        return $this->mongo->executeBulkWrite($this->database . "." . $collection, $BulkWrite);
    }

}
